package com.Example.cert;

import com.utils.KeyUtil;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.RFC4519Style;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.io.pem.PemObject;

import java.io.StringWriter;
import java.io.Writer;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;


/**
 * @author zyf
 */
public class GenerateX509V3 {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static long serialNumber = System.currentTimeMillis();

    public static void main(String[] args) throws Exception {

        // 构建密钥对
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC","BC");
//        kpGen.initialize(new ECNamedCurveGenParameterSpec("secp256k1"));
        kpGen.initialize(ECNamedCurveTable.getParameterSpec("secp256k1"));
        KeyPair keyPair = kpGen.generateKeyPair();

        // 将 publicKey 写入到String，读的时候用PEMParser + JcaPEMKeyConverter()
        // 写公钥
        Writer pubWriter = new StringWriter();
        JcaPEMWriter pemPubWri = new JcaPEMWriter(pubWriter);
        pemPubWri.writeObject(keyPair.getPublic());
        pemPubWri.close(); pubWriter.close();
        String pub_pem = pubWriter.toString();
        System.out.println(pub_pem);

        // 写私钥
        Writer priWriter = new StringWriter();
        JcaPEMWriter pemPriWri = new JcaPEMWriter(priWriter);
        pemPriWri.writeObject(new PemObject("PRIVATE KEY",keyPair.getPrivate().getEncoded()));
//        pemPriWri.close(); priWriter.close();
//        String pri_pem =priWriter.toString();

        // 写加密私钥,可选运用不同的加密算法
        String priPem = KeyUtil.generatePrikeyPemWithPass(keyPair.getPrivate(),"123",  JcaPKCS8Generator.DES3_CBC, JcaPKCS8Generator.PRF_HMACSHA1);
        System.out.println(keyPair.getPrivate());
        PrivateKey privateKey = KeyUtil.generatePrikeyWithPem(priPem,"123");
        System.out.println(privateKey);

        // 构建自签名证书
        X509Certificate certificate = makeV3Certificate("SHA256WITHECDSA",keyPair);
        // 确认为自签名证书,check isSelfSigned
        certificate.verify(keyPair.getPublic(),"BC");
        System.out.println(certificate);

        // 将私钥与证书写到一起，方便后面初始化client进行构建交易
        pemPriWri.writeObject(certificate);
        pemPriWri.close(); priWriter.close();
        String pri_cert = priWriter.toString();
        System.out.println(pri_cert);
    }

    /**
     * 若干小时后的日期
     * @param hoursInFuture
     * @return
     */
    // 计算日期
    public static Date calculateDate(int hoursInFuture) {
        long secs = System.currentTimeMillis() / 1000;
        return new Date((secs + (hoursInFuture * 60 * 60)) * 1000);
    }

    // 计算序列号
    public static BigInteger calculateSerialNumber() {
        return BigInteger.valueOf(serialNumber ++);
    }


    /**
     * 自签名证书
     * @param sigAlg     签名算法
     * @param keyPair    密钥对
     * @return
     * @throws CertificateException
     * @throws OperatorCreationException
     */
    public static X509Certificate makeV3Certificate(String sigAlg, KeyPair keyPair) throws CertificateException, OperatorCreationException {
        X500NameBuilder x500Bldr = new X500NameBuilder(RFC4519Style.INSTANCE);
        X500Name x500Name = x500Bldr.addRDN(RFC4519Style.c,"cn")
                .addRDN(RFC4519Style.st,"bj")
                .addRDN(RFC4519Style.o,"iscas")
                .addRDN(RFC4519Style.ou,"iscas")
                .addRDN(RFC4519Style.cn, "repchain")
                .build();
        X509v3CertificateBuilder x509v3CertificateBuilder = new JcaX509v3CertificateBuilder(
                x500Name,
                calculateSerialNumber(),
                calculateDate(0),
                calculateDate(24 * 365),
                x500Name,
                keyPair.getPublic()
        );
        ContentSigner signer = new JcaContentSignerBuilder(sigAlg).setProvider("BC").build(keyPair.getPrivate());

        return new JcaX509CertificateConverter().setProvider("BC").getCertificate(x509v3CertificateBuilder.build(signer));
    }

}
